Skip to content

feat: M7 mobile-responsive UI — bottom-docked panel, 44px tap targets, landscape height cap - #18

Merged
DecodedXR merged 3 commits into
mainfrom
feat/m7-mobile-responsive
Jul 8, 2026
Merged

DecodedXR merged 3 commits into
mainfrom
feat/m7-mobile-responsive

Conversation

@DecodedXR

Copy link
Copy Markdown
Owner

Milestone 7 — Mobile-responsive UI

Task (STATUS.md NEXT): the app was unusable on an iPhone — the fixed top-left #controls panel (~236px) collided with the centered #app-title on a ~390px screen, and the controls were far below finger size.

Fix: pure CSS — one @media (max-width: 640px) block in src/style.css. No JS or markup changes anywhere; src/main.js, the render loop, and the M4 decoupling machinery are byte-identical, and desktop above the breakpoint is untouched (every new rule lives inside the query).

  • #controls docks to the bottom edge full-width (safe-area padding), sliders stretch, buttons + file input meet the 44px tap minimum.
  • #build-credit moves up under the top-center title, since the panel now owns the bottom edge.
  • Landscape phones (≤640px wide, ~320–360px tall — verifier finding): the panel caps at calc(100dvh − 88px) (vh fallback) and scrolls internally, so it never reaches the top chrome or exceeds the viewport.

Test harness: new mobile-chromium Playwright project (Chromium, 390×844, DPR 3, hasTouch, isMobile — no WebKit; CI provisions Chromium only) running tests/mobile.spec.js: boot-loader dismissal, pairwise non-intersection of the fixed chrome, every control fully in-viewport, ≥44px tap targets, the landscape cap/scroll contract at 640×360 with a grown status line, and touch orbit driven via CDP Input.dispatchTouchEvent (trusted pointerType:'touch' events — a synthetic dispatchEvent would hit OrbitControls 0.185's unguarded setPointerCapture and throw NotFoundError; verified from the installed source).

RED tests: portrait overlap RED on pre-M7 CSS with exactly the reported geometry (#controls x 12–248 vs #app-title x 101–289); landscape RED pre-cap on the verifier's geometry (panel y −16 at 568×320). Both proven non-tautological by reverting only the production CSS (tests kept) → RED → restore → green.

Verifier: one adversarial pass REFUTED the initial portrait-only design (landscape phones inside the same breakpoint: overlap + off-viewport panel); after the height-cap fix a re-verification PASSED the full layout matrix (390×844, 640×360, 568×320, grown-panel and webcam-running states), confirmed the fix is load-bearing, clipped rows reachable with 44px targets intact, and panel scrolling doesn't leak into orbit.

Review: inline pass — no unresolved findings. One pre-existing, out-of-scope note carried forward: desktop widths 641–684px still show the controls×title overlap that exists on main (the "desktop visually unchanged" clause keeps it out of M7; worth a future queue entry if it bothers anyone).

Local suite: 20/20 green (npm test -- --workers=1), verified visually on real builds (mobile portrait + desktop screenshots — desktop byte-identical).

Rollback HEAD: 58d3b9a.

Autopilot: STATUS.md M7 (Mobile-responsive UI)

🤖 Generated with Claude Code

DecodedXR and others added 3 commits July 8, 2026 11:33
On a phone-width viewport the desktop chrome broke down: the fixed
top-left #controls panel (~236px) collided with the centered #app-title
on a 390px screen, and the buttons were far below finger size — the
user's "unusable on iPhone" report.

Pure-CSS fix, all inside a new @media (max-width: 640px) block in
src/style.css (desktop layout above the breakpoint is untouched, and no
JS changed at all):

- #controls docks to the bottom edge, full width, with safe-area
  padding; sliders stretch; buttons and the file input meet the 44px
  minimum tap target.
- #build-credit moves up under the top-center title block, since the
  docked panel now owns the bottom edge.

Test harness: a new `mobile-chromium` Playwright project (390x844,
DPR 3, hasTouch, isMobile, same SwiftShader args) runs the new
tests/mobile.spec.js: boot-loader dismissal, pairwise no-intersection
among the fixed chrome, every control fully in-viewport, tap targets
>= 44px, and a real touch drag (CDP Input.dispatchTouchEvent — trusted
pointerType:'touch' events; synthetic dispatchEvent would hit
OrbitControls' unguarded setPointerCapture and throw NotFoundError)
orbits the camera. RED pre-fix on the overlap assert with exactly the
reported geometry.

Autopilot: STATUS.md M7 (Mobile-responsive UI)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…er finding)

At 640x360 / 568x320 the bottom-docked #controls panel (~336px tall) sat
inside the same <=640px breakpoint, overlapped #app-title/#build-credit,
and with a grown status line extended above the viewport top where
body{overflow:hidden} made rows unreachable. Cap it at
calc(100dvh - 88px) (vh fallback) with internal overflow-y scrolling.
New landscape test pins no-overlap + in-viewport + scroll-reachability;
RED pre-fix on the verifier geometry.

Autopilot: STATUS.md M7 (Mobile-responsive UI)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Folds the 2026-07-08 queue rebuild (M7-M10 from user direction) and the
M7 completion record into one atomic update. Rollback HEAD for M7:
58d3b9a.

Autopilot: STATUS.md M7 (Mobile-responsive UI)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@DecodedXR
DecodedXR merged commit c2f0a9f into main Jul 8, 2026
2 checks passed
@DecodedXR
DecodedXR deleted the feat/m7-mobile-responsive branch July 8, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant